/*
 * XDevice RPC Client Implementation
 */

#include <xdevice/rpc.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>

static int send_request(int client_fd, uint32_t type, uint32_t request_id, 
                       const void* data, uint32_t data_length) {
    xdevice_rpc_message_t* request = malloc(sizeof(xdevice_rpc_message_t) + data_length);
    if (!request) {
        return -1;
    }
    
    request->type = type;
    request->request_id = request_id;
    request->data_length = data_length;
    
    if (data && data_length > 0) {
        memcpy(request->data, data, data_length);
    }
    
    ssize_t total_size = sizeof(xdevice_rpc_message_t) + data_length;
    ssize_t sent = send(client_fd, request, total_size, 0);
    
    free(request);
    return (sent == total_size) ? 0 : -1;
}

static int receive_response(int client_fd, uint32_t expected_request_id, 
                           void* data, uint32_t max_data_length) {
    xdevice_rpc_message_t response;
    
    // Receive response header
    ssize_t received = recv(client_fd, &response, sizeof(response), 0);
    if (received != sizeof(response)) {
        return -1;
    }
    
    if (response.request_id != expected_request_id) {
        return -1;
    }
    
    // Receive response data
    if (response.data_length > 0 && data && max_data_length > 0) {
        uint32_t to_read = (response.data_length > max_data_length) ? 
                          max_data_length : response.data_length;
        
        received = recv(client_fd, data, to_read, 0);
        if (received != to_read) {
            return -1;
        }
    }
    
    return response.type;  // Return result code
}

/* Public API implementation */
int xdevice_rpc_client_connect(const char* socket_path) {
    int client_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (client_fd < 0) {
        perror("socket");
        return -1;
    }
    
    struct sockaddr_un server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sun_family = AF_UNIX;
    strncpy(server_addr.sun_path, socket_path, sizeof(server_addr.sun_path) - 1);
    
    if (connect(client_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("connect");
        close(client_fd);
        return -1;
    }
    
    return client_fd;
}

void xdevice_rpc_client_disconnect(int client_fd) {
    if (client_fd >= 0) {
        close(client_fd);
    }
}

int xdevice_rpc_get_node_status(int client_fd, xdevice_rpc_node_status_t* status) {
    if (!status) {
        return XDEVICE_RPC_INVALID_REQUEST;
    }
    
    static uint32_t request_id = 1;
    uint32_t current_request = request_id++;
    
    if (send_request(client_fd, XDEVICE_RPC_GET_STATUS, current_request, NULL, 0) != 0) {
        return XDEVICE_RPC_ERROR;
    }
    
    int result = receive_response(client_fd, current_request, status, sizeof(*status));
    return (result == XDEVICE_RPC_OK) ? XDEVICE_RPC_OK : XDEVICE_RPC_ERROR;
}

int xdevice_rpc_get_cluster_status(int client_fd, xdevice_rpc_cluster_status_t* status) {
    if (!status) {
        return XDEVICE_RPC_INVALID_REQUEST;
    }
    
    static uint32_t request_id = 1;
    uint32_t current_request = request_id++;
    
    if (send_request(client_fd, XDEVICE_RPC_GET_CLUSTER_INFO, current_request, NULL, 0) != 0) {
        return XDEVICE_RPC_ERROR;
    }
    
    int result = receive_response(client_fd, current_request, status, sizeof(*status));
    return (result == XDEVICE_RPC_OK) ? XDEVICE_RPC_OK : XDEVICE_RPC_ERROR;
}

int xdevice_rpc_get_log_info(int client_fd, xdevice_rpc_log_info_t* log_info) {
    if (!log_info) {
        return XDEVICE_RPC_INVALID_REQUEST;
    }
    
    static uint32_t request_id = 1;
    uint32_t current_request = request_id++;
    
    if (send_request(client_fd, XDEVICE_RPC_GET_RAFT_LOG, current_request, NULL, 0) != 0) {
        return XDEVICE_RPC_ERROR;
    }
    
    int result = receive_response(client_fd, current_request, log_info, sizeof(*log_info));
    return (result == XDEVICE_RPC_OK) ? XDEVICE_RPC_OK : XDEVICE_RPC_ERROR;
}

int xdevice_rpc_get_device_list(int client_fd, xdevice_rpc_device_list_t* device_list) {
    if (!device_list) {
        return XDEVICE_RPC_INVALID_REQUEST;
    }
    
    static uint32_t request_id = 1;
    uint32_t current_request = request_id++;
    
    if (send_request(client_fd, XDEVICE_RPC_GET_DEVICES, current_request, NULL, 0) != 0) {
        return XDEVICE_RPC_ERROR;
    }
    
    int result = receive_response(client_fd, current_request, device_list, sizeof(*device_list));
    return (result == XDEVICE_RPC_OK) ? XDEVICE_RPC_OK : XDEVICE_RPC_ERROR;
}

int xdevice_rpc_ping(int client_fd) {
    static uint32_t request_id = 1;
    uint32_t current_request = request_id++;
    
    if (send_request(client_fd, XDEVICE_RPC_PING, current_request, NULL, 0) != 0) {
        return XDEVICE_RPC_ERROR;
    }
    
    char response[16];
    int result = receive_response(client_fd, current_request, response, sizeof(response));
    return (result == XDEVICE_RPC_OK) ? XDEVICE_RPC_OK : XDEVICE_RPC_ERROR;
}
